package com.fsserver.config.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fsserver.common.utils.MDCTraceUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Web 日志切面
 * 记录接口请求和响应信息
 * 
 * @author fsserver
 * @date 2025-03-13
 */
@Aspect
@Component
public class WebLogAspect {
    private static final Logger log = LoggerFactory.getLogger(WebLogAspect.class);
    
    @Autowired
    private ObjectMapper objectMapper;
    
    /**
     * 定义切点
     * 拦截所有 Controller 类中的方法
     */
    @Pointcut("execution(* com.fsserver.web..*Controller.*(..))")
    public void webLog() {}
    
    /**
     * 前置通知：在方法执行前记录请求信息
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                
                // 记录请求信息
                log.info("========== 接口请求开始 ==========");
                log.info("请求URL: {}", request.getRequestURL().toString());
                log.info("请求方法: {}", request.getMethod());
                log.info("请求IP: {}", getClientIp(request));
                log.info("请求类方法: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
                
                // 过滤掉 HttpServletRequest、HttpServletResponse 和 MultipartFile 类型的参数
                List<Object> args = Arrays.stream(joinPoint.getArgs())
                        .filter(arg -> !(arg instanceof HttpServletRequest) && 
                                       !(arg instanceof HttpServletResponse) && 
                                       !(arg instanceof MultipartFile))
                        .collect(Collectors.toList());
                
                // 记录请求参数
                if (!args.isEmpty()) {
                    try {
                        log.info("请求参数: {}", objectMapper.writeValueAsString(args));
                    } catch (Exception e) {
                        log.info("请求参数: {}", args);
                    }
                }
                
                // 设置业务ID（如果有）
                String orderNo = request.getParameter("orderNo");
                if (orderNo != null && !orderNo.isEmpty()) {
                    MDCTraceUtils.setBusinessId(orderNo);
                }
            }
        } catch (Exception e) {
            log.error("记录请求信息异常", e);
        }
    }
    
    /**
     * 环绕通知：记录方法执行时间和响应结果
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
            return result;
        } finally {
            long endTime = System.currentTimeMillis();
            try {
                // 记录响应结果和执行时间
                if (result != null) {
                    log.info("响应结果: {}", objectMapper.writeValueAsString(result));
                }
                log.info("执行时间: {} ms", (endTime - startTime));
                log.info("========== 接口请求结束 ==========");
            } catch (Exception e) {
                log.error("记录响应信息异常", e);
            }
        }
    }
    
    /**
     * 异常通知：记录异常信息
     */
    @AfterThrowing(pointcut = "webLog()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        log.error("接口异常: {}", e.getMessage(), e);
        log.info("========== 接口请求异常结束 ==========");
    }
    
    /**
     * 获取客户端真实IP
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 多个代理的情况，第一个IP为客户端真实IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }
} 